E) Copy Elision

복사 생략(Copy Elision)
C++11에서 공식화된 기능으로 컴파일러가 복사 또는 이동 연산자를 회피 할 수 있으면 회피하는 것을 허용하는 방식이다.
특정 조건을 만족하면, 컴파일러가 임의로 최적화를 위해 복사 및 이동 연산을 생략한다.

- Return Value Optimization(반환값 최적화)인 경우
- Passing Temporary as Value
Return Value Optimization/ Named Return Value Optimization
RVO/NRVO 는 함수의 반환값이 특정 객체의 값 형식일 때, 복사 생성을 회피할 수 있는 컴파일러 최적화를 의미한다.
#include <iostream>
struct Foo{
Foo(void){ std::cout<<"Constructed\n"; }
Foo(const Foo&){ std::cout<<"Copy-Constructed\n"; }
Foo(Foo&&){std::cout<<"Move-Constructed\n"; }
~Foo(void){ std::cout<<"Destructed\n"; }
};
// Return Value Optimization(RVO)
Foo RVO_F(void){
return Foo();
}
// Named Return Value Optimization(NRVO)
Foo NRVO_F(void){
Foo foo;
return foo;
}
int main(void){
{
Foo rvo_foo=RVO_F();
}
{
Foo nrvo_foo=NRVO_F();
}
return 0;
}

Constructed

Destructed

Constructed

Destructed

복사 생략이 되지 않다면, RVO에 대해서 return을 통해 값이 복사될 때, 사용되는 복사생성자가 호출되어야 한다.
NRVO의 경우, foo를 rvo_foo로 이동할 때, 복사 생성자가 호출되며, 함수가 끝난 후, foo 또한 소멸자가 호출되어
2번 소멸자가 호출되어야 한다.
C++ Chapter 9.10 : 복사 생성자, 복사 생략, 리턴값 최적화 - 평생 공부 블로그 : Today I Learned‍ 🌙
Passing Temporary as Value
함수 인자로써 임시 객체를 값으로 전달하는 경우
#include <iostream>
struct Foo{
Foo(void){ std::cout<<"Constructed\n"; }
Foo(const Foo&){ std::cout<<"Copy-Constructed\n"; }
Foo(Foo&&){std::cout<<"Move-Constructed\n"; }
~Foo(void){ std::cout<<"Destructed\n"; }
};
void f(Foo f){
std::cout<<"Fn\n";
}
int main(void){
f(Foo());
return 0;
}

Constructed

Fn

Destructed

복사 생략이 되지 않았다면, f 함수에 인자로 전달하기 위해 Rvalue Foo를 이용해서 이동생성자를 호출해야 한다.
(Rvalue Foo()에서 f로 값을 이동)
하지만, 컴파일러는 최적화를 통해 임시 객체가 참조되는 대상 없이 함수의 인자로 넘어갈 때(값으로 전달될 때),
복사/이동 생성자가 생략된다.